<?php

namespace app\providers\webSocket;

use think\App;
use auth\Auths;
use help\IpHelp;
use think\Event;
use Swoole\Timer;
use think\Config;
use Swoole\Server;
use think\Request;
use Swoole\Websocket\Frame;
use think\swoole\Websocket;
use app\resources\model\Nodes;
use think\swoole\websocket\Room;
use app\resources\model\WebSocket as WebSocketModel;
use app\resources\service\WebSocketService;
use think\swoole\websocket\socketio\Packet;
use think\swoole\websocket\socketio\Parser;
use think\swoole\websocket\socketio\EnginePacket;

class Handler extends Websocket
{

    protected $pingIntervalTimer = 0;
    protected $pingInterval;
    protected $webSocketService;

    public function __construct(App $app, Server $server, Room $room, Event $event, Config $config)
    {
        $this->config       = $config;
        $this->pingInterval = $this->config->get('swoole.websocket.ping_interval', 2500);
        $this->webSocketService = new WebSocketService($app,$server,$room, $event, $config);
        parent::__construct($app, $server, $room, $event);
    }

    /**
     * "onOpen" listener.
     *
     * @param int $fd
     * @param Request $request
     */
    public function onOpen($fd, Request $request)
    {
        $action = $request->get('action');
        switch ($action) {
            /**
             * 网页访问
             */
            case 'web':
                // 获取用户信息
                $token = $request->get('token');
                if (!$tokens = Auths::instance('api')->getTokenInfo($token)) {
                    if (!$tokens = Auths::instance('admin')->getTokenInfo($token)) {
                        $this->close();
                        return;
                    }
                }
                $userId = $tokens['info']['user_id'] ?? $tokens['info']['id'] ?? 0;
                $socket = WebSocketModel::where('fd',$fd)->findOrEmpty();
                break;
            /**
             * 系统消息
             */
            case 'system':
                if ( !Nodes::decryptSecretKey($request->get('ssh')) ) {
                    $this->close();
                    return;
                }
                $userId = IpHelp::getOnlineIp();
                $socket = WebSocketModel::where('uid',$userId)->findOrEmpty();
                break;
            /**
            * 不允许链接
            */
            default:
                $this->close();
                return;
        }
        // 记录
        if($userId){
            $socket->fd = $fd;
            $socket->uid = $userId;
            $socket->action = $action;
            $socket->save();
            // 重补离线消息
            try {
                $this->webSocketService->onOpen($fd,$request);
            } catch (\Throwable $th) {
                //throw $th;
            }
            // 设置超时
            $this->schedulePing();
        }
    }

    /**
     * "onMessage" listener.
     *
     * @param Frame $frame
     */
    public function onMessage(Frame $frame)
    {
        try {
            $this->webSocketService->onMessage($frame);
        } catch (\Throwable $th) {
            //throw $th;
        }
    }

    /**
     * "onClose" listener.
     *
     * @param int $fd
     * @param int $reactorId
     */
    public function onClose($fd, $reactorId)
    {
        $res = WebSocketModel::where('fd',$fd)->delete();
        if($res){
            // 
            try {
                $this->webSocketService->onClose($fd, $reactorId);
            } catch (\Throwable $th) {
                //throw $th;
            }
            Timer::clear($this->pingIntervalTimer);
        }
    }

    // 发送消息
    public function push($data)
    {
        if ($data instanceof Packet) {
            $data = EnginePacket::message(Parser::encode($data));
        }
        if ($data instanceof EnginePacket) {
            $data = $data->toString();
        }
        return parent::push($data);
    }

    // 设置心跳消息
    protected function schedulePing()
    {
        $this->push('心跳消息'.time());
        Timer::clear($this->pingIntervalTimer);
        $this->pingIntervalTimer = Timer::after($this->pingInterval, function () {
            $this->schedulePing();
        });
    }

}
